package es.deusto.ingenieria.is.search.touringproblem.formulation;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import es.deusto.ingenieria.is.search.algorithms.Node;
import es.deusto.ingenieria.is.search.algorithms.SearchMethod;
import es.deusto.ingenieria.is.search.formulation.Problem;
import es.deusto.ingenieria.is.search.formulation.State;
import es.deusto.ingenieria.is.search.xml.StateXMLReader;

/**
 * @author irene.gallardo@opendeusto.es y daphne.aberasturi@opendeusto.es
 * Crea el problema inicial a resolver
 *
 */
public class TouringProblem extends Problem {
	
	public TouringProblem() {
		super();
	}
	
	//Crea un estado inicial del problema
	public State gatherInitialPercepts() {
		//lee los datos introducidos en el archivo xml
		StateXMLReader stateXMLReader = new MapReader("data/tourProblem.xml"); 	
		EnvironmentMap environ=(EnvironmentMap) stateXMLReader.getState();
		return environ;
	}
	
	/*Crea las instancias de los operadores.
	 * En total 7 instancias al no incluir en la lista de visitados ni la ciudad inicio ni la ciudad final.
	 */
	public void createOperators() {
		EnvironmentMap environ= (EnvironmentMap) this.getInitialStates().get(0);
		for(int i=0;i<environ.getDistancias().size();i++) {
			if(!environ.getDistancias().get(i).getType().equals("start")&&(!environ.getDistancias().get(i).getType().equals("end"))){
				AddCity addC=new AddCity(environ.getDistancias().get(i));
				this.addOperator(addC);
				
			}
		}
		
		//Desordena los operadores introducidos
		Collections.shuffle(this.getOperators());
		//System.out.println(this.getOperators());
		
	}

	/* Comprueba que el estado que recibe por parametro es un estado final
	 * comparando los tamanios de la lista de ciudades visitadas con la lista total de ciudades.
	 * En caso de que el tamanios de la primera sea igual al de la segunda sin la ciudad inicio y fin, 
	 * se devolveria true, en caso contrario, false
	 */
	public boolean isFinalState(State state) {
		if (state != null && state != null && state instanceof EnvironmentMap) {
			EnvironmentMap envMap = (EnvironmentMap)state;

			if(envMap.getVisitadas().size()==envMap.getDistancias().size()-2){
				return true;
			}
		}
			return false;
		
	}
	
	
	//Resuelve el problema
	public void solve(SearchMethod searchMethod) {		
		SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.S");
		Date beginDate = GregorianCalendar.getInstance().getTime();
		System.out.println("\n* Start '" + searchMethod.getClass().getSimpleName() + "' (" + formatter.format(beginDate) + ")");				
		Node finalNode = searchMethod.search(this, this.getInitialStates().get(0));
		Date endDate = GregorianCalendar.getInstance().getTime();		
		System.out.println("* End   '" + searchMethod.getClass().getSimpleName() + "' (" + formatter.format(endDate) + ")");
		
		long miliseconds = (int) Math.abs(beginDate.getTime() - endDate.getTime());
		long seconds = miliseconds / 1000;
		miliseconds %= 1000;		
		long minutes = seconds / 60;
		seconds %= 60;
		long hours = minutes / 60;
		minutes %= 60;
		
		String time = "* Search lasts: ";
		time += (hours > 0) ? hours + " h " : " ";
		time += (minutes > 0) ? minutes + " m " : " ";
		time += (seconds > 0) ? seconds + "s " : " ";
		time += (miliseconds > 0) ? miliseconds + "ms " : " ";
		
		System.out.println(time);
		
		if (finalNode != null) {
			System.out.println("\n- Solution found!     :)");
			List<String> operators = new ArrayList<String>();
			searchMethod.solutionPath(finalNode, operators);
			searchMethod.createSolutionLog(operators);			
			System.out.println("- Final state:" + finalNode.getState());
		} else {
			System.out.println("\n- Unable to find the solution!     :(");
		}
	}
	
}